import { User } from '@app/db/models/user.model';
import { RsaService } from '@app/rsa/rsa.service';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ReturnModelType } from '@typegoose/typegoose';
import { InjectModel } from 'nestjs-typegoose';
import { Strategy, IStrategyOptions } from 'passport-local';
import * as bcrypt from 'bcrypt';

import { GUARD_NAME } from './local.guard';

@Injectable()
export class LocalStrategy
  extends PassportStrategy(Strategy, GUARD_NAME)
  implements Strategy
{
  constructor(
    @InjectModel(User) readonly userModel: ReturnModelType<typeof User>,
    private readonly rsaService: RsaService,
  ) {
    super({
      // 可以自定义字段名称
      // 以下是默认的
      // usernameField: 'username',
      // passwordField: 'password',
      //
      // 假设业务中使用邮箱作为用户名
      // usernameField:'email'

      // 本例中使用自定义的字段名，前端在传参时也要使用这个字段
      usernameField: 'username_field',
      passwordField: 'password_field',
    } as IStrategyOptions);
  }

  /**
   * @description 实现校验方法
   * @param username 对于前端传入的 username_field
   * @param rsaEncryptedPassword 对于前端传入的 password_field
   * @param cb 一个回调函数
   */
  async validate(username: string, rsaEncryptedPassword: string) {
    console.log('local strategy', username, rsaEncryptedPassword);

    // 从数据库中通过用户名来查询匹配的用户
    // 注意：需要把返回的文档对象转化成普通对象
    // 否则在生成 token 的时候（JwtService.sign）会提示错误
    const matchedUser = (await this.userModel.findOne({ username })).toObject();

    if (matchedUser) {
      // 明文
      const password = this.rsaService.decrypt(rsaEncryptedPassword);
      // hash后的密文（创建用户时保存到数据库）
      const hashedPassword = matchedUser.password;

      // 对比明文密码和保存的用户密码是否匹配
      const matched = await bcrypt.compare(password, hashedPassword);
      if (matched) {
        return matchedUser;
      } else {
        throw new UnauthorizedException('密码错误');
      }
    } else {
      throw new UnauthorizedException('用户不存在');
    }
  }
}
